package com.app.module.camera.renderer

import android.content.res.Resources
import android.opengl.GLES20
import com.app.module.camera.core.FrameBuffer
import com.app.module.camera.utils.MatrixUtils
import com.app.module.camera.utils.ShaderUtils
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import java.util.*

abstract class AbstractRenderer : GLRenderer {
    companion object {
        val BASE_VERT = """attribute vec4 aVertexCo;
                            attribute vec2 aTextureCo;

                            uniform mat4 uVertexMatrix;
                            uniform mat4 uTextureMatrix;

                            varying vec2 vTextureCo;

                            void main(){
                                gl_Position = uVertexMatrix*aVertexCo;
                                vTextureCo = (uTextureMatrix*vec4(aTextureCo,0,1)).xy;
                            }"""

        val BASE_FRAG = """precision mediump float;
            
                            varying vec2 vTextureCo;
                            uniform sampler2D uTexture;
                            
                            void main() {
                                gl_FragColor = texture2D( uTexture, vTextureCo);
                            }"""
    }

    protected var mWidth = 0
    protected var mHeight = 0

    private var mVertex: String
    private var mFragment: String

    private var mVertexBuffer: FloatBuffer
    private var mTextureBuffer: FloatBuffer

    private var mVertexMatrix = MatrixUtils.getOriginalMatrix()
    private var mTextureMatrix = MatrixUtils.getOriginalMatrix()

    protected var mGLProgram = 0
    protected var mGLVertexCo = 0
    protected var mGLTextureCo = 0
    protected var mGLVertexMatrix = 0
    protected var mGLTextureMatrix = 0
    protected var mGLTexture = 0

    private var isUseExpandConfig = false

    private var mFrameTemp: FrameBuffer
    private val mTasks = LinkedList<Runnable>()

    protected constructor(resource: Resources, vertexPath: String, fragmentPath: String)
        : this(ShaderUtils.readText(resource, vertexPath)
        ?: BASE_VERT, ShaderUtils.readText(resource, fragmentPath) ?: BASE_FRAG)

    protected constructor(vertex: String, fragment: String) {
        this.mVertex = vertex
        this.mFragment = fragment
        this.mFrameTemp = FrameBuffer()
        this.mVertexBuffer = ByteBuffer.allocateDirect(32)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
            .also {
                it.put(MatrixUtils.getOriginalVertexCo())
                it.position(0)
            }
        this.mTextureBuffer = ByteBuffer.allocateDirect(32)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer().also {
                it.put(MatrixUtils.getOriginalTextureCo())
                it.position(0)
            }
    }

    fun setVertexBuffer(vertexArray: FloatArray) {
        mVertexBuffer.also {
            it.clear()
            it.put(vertexArray)
            it.position(0)
        }
    }

    fun setTextureBuffer(testureArray: FloatArray) {
        mTextureBuffer.also {
            it.clear()
            it.put(testureArray)
            it.position(0)
        }
    }

    fun setVertexBuffer(vertexBuffer: FloatBuffer) {
        mVertexBuffer = vertexBuffer
    }

    fun setTextureBuffer(textureBuffer: FloatBuffer) {
        mTextureBuffer = textureBuffer
    }

    fun setVertexMatrix(matrix: FloatArray) {
        mVertexMatrix = matrix
    }

    fun setTextureMatrix(matrix: FloatArray) {
        mTextureMatrix = matrix
    }

    fun getVertexMatrix(): FloatArray {
        return mVertexMatrix
    }

    open fun getTextureMatrix(): FloatArray {
        return mTextureMatrix
    }

    fun needUseExpandConfig(need: Boolean) {
        isUseExpandConfig = need
    }

    fun runOnGLThread(runnable: Runnable) {
        mTasks.addLast(runnable)
    }

    override fun create() {
        mGLProgram = ShaderUtils.createGLProgram(mVertex, mFragment)

        mGLVertexCo = GLES20.glGetAttribLocation(mGLProgram, "aVertexCo")
        mGLTextureCo = GLES20.glGetAttribLocation(mGLProgram, "aTextureCo")

        mGLVertexMatrix = GLES20.glGetUniformLocation(mGLProgram, "uVertexMatrix")
        mGLTextureMatrix = GLES20.glGetUniformLocation(mGLProgram, "uTextureMatrix")
        mGLTexture = GLES20.glGetUniformLocation(mGLProgram, "uTexture")

        if (isUseExpandConfig) {
            onCreateExpandConfig()
        }
    }

    protected open fun onCreateExpandConfig() {

    }

    override fun sizeChanged(width: Int, height: Int) {
        this.mWidth = width
        this.mHeight = height
        this.mFrameTemp.destroyFrameBuffer()
    }


    override fun drawToTexture(texture: Int): Int {
        mFrameTemp.bindFrameBuffer(mWidth, mHeight)
        draw(texture)
        mFrameTemp.unBindFrameBuffer()
        return mFrameTemp.getCacheTextureId()
    }

    override fun draw(texture: Int) {
        onClear()
        onUseProgram()
        onSetExpandData()
        onBindTexture(texture)
        onDraw()
        onComplete()
    }
    
    protected open fun onClear() {
        GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f)
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT)
    }

    protected open fun onUseProgram() {
        GLES20.glUseProgram(mGLProgram)
        onTaskExec()
    }

    protected open fun onTaskExec() {
        while (!mTasks.isEmpty()) {
            mTasks.removeFirst().run()
        }
    }

    protected open fun onSetExpandData() {
        GLES20.glUniformMatrix4fv(mGLVertexMatrix, 1, false, mVertexMatrix, 0)
        GLES20.glUniformMatrix4fv(mGLTextureMatrix, 1, false, mTextureMatrix, 0)
        if (isUseExpandConfig) {
            onDrawExpandConfig()
        }
    }

    protected open fun onDrawExpandConfig(){
        
    }

    protected open fun onBindTexture(textureId: Int) {
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId)
        GLES20.glUniform1i(mGLTexture, 0)
    }

    protected open fun onDraw() {
        GLES20.glEnableVertexAttribArray(mGLVertexCo)
        GLES20.glVertexAttribPointer(mGLVertexCo, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer)
        GLES20.glEnableVertexAttribArray(mGLTextureCo)
        GLES20.glVertexAttribPointer(mGLTextureCo, 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer)
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
        GLES20.glDisableVertexAttribArray(mGLVertexCo)
        GLES20.glDisableVertexAttribArray(mGLTextureCo)
    }

    protected open fun onComplete() {

    }

    override fun destroy() {
        GLES20.glDeleteProgram(mGLProgram)
        mFrameTemp.destroyFrameBuffer()
    }
}
